import weakref
import pyglet
from pyglet.image import Animation
from pyglet.sprite import Sprite
from pyglet.gl import *
from pymunk import inf, Vec2d, Body, Circle, Poly
from util import *
from baby import Baby
from gameprops import Ladder

class Gator(object):
    def __init__(self, game_map):
        self.position = Vec2d(0,0)
        frames = [animation_frame('graphics/gator_right1.png', 93, 55, 0.15),
                  animation_frame('graphics/gator_right2.png', 95, 55, 0.15),
                  animation_frame('graphics/gator_right3.png', 97, 55, 0.15),
                  animation_frame('graphics/gator_right4.png', 97, 55, 0.15),
                  animation_frame('graphics/gator_right3.png', 97, 55, 0.15),
                  animation_frame('graphics/gator_right2.png', 95, 55, 0.15),]
        self.sprite_right = Sprite(Animation(frames))

        frames = [animation_frame('graphics/gator_left1.png', 70, 55, 0.15),
                  animation_frame('graphics/gator_left2.png', 63, 55, 0.15),
                  animation_frame('graphics/gator_left3.png', 64, 55, 0.15),
                  animation_frame('graphics/gator_left4.png', 66, 55, 0.15),
                  animation_frame('graphics/gator_left3.png', 64, 55, 0.15),
                  animation_frame('graphics/gator_left2.png', 63, 55, 0.15),]
        self.sprite_left = Sprite(Animation(frames))

        frames = [animation_frame('graphics/gator_idle_right1.png', 97, 55, 0.3),
                  animation_frame('graphics/gator_idle_right2.png', 93, 55, 0.2),
                  animation_frame('graphics/gator_idle_right3.png', 93, 55, 0.2),
                  animation_frame('graphics/gator_idle_right2.png', 93, 55, 0.2),]
        self.sprite_idle_right = Sprite(Animation(frames))

        frames = [animation_frame('graphics/gator_idle_left1.png', 61, 55, 0.3),
                  animation_frame('graphics/gator_idle_left2.png', 62, 55, 0.2),
                  animation_frame('graphics/gator_idle_left3.png', 61, 55, 0.2),
                  animation_frame('graphics/gator_idle_left2.png', 62, 55, 0.2),]
        self.sprite_idle_left = Sprite(Animation(frames))

        frames = [animation_frame('graphics/gator_climb1.png', 67, 72, 0.3),
                  animation_frame('graphics/gator_climb2.png', 61, 72, 0.2),
                  animation_frame('graphics/gator_climb3.png', 57, 72, 0.2),
                  animation_frame('graphics/gator_climb2.png', 61, 72, 0.2),]
        self.sprite_climb = Sprite(Animation(frames))

        self.sprite = self.sprite_idle_right
        self.face_direction = 'right'

        self.map = weakref.proxy(game_map)
        self.body = Body(10, inf)
        self.body.position = (210, 100)

        # Attach shape for body.
        body_vertices = [(-50,-45), (-50,70), (50,70), (50,-45)]
        self.body_shape = Poly(self.body, body_vertices)
        self.body_shape.friction = 0.5
        self.body_shape.elasticity = 0.5
        self.body_shape.collision_type = 1
        self.body_shape.object = weakref.ref(self)

        # Attach shape for hat.
        hat_vertices = [(-25,0), (-30,112), (30,112), (25,0)]
        self.hat_shape = Poly(self.body, hat_vertices)
        self.hat_shape.friction = 0.5
        self.hat_shape.elasticity = 0.5
        self.hat_shape.collision_type = 1
        self.hat_shape.object = weakref.ref(self)
        
        # Add everything to space.
        self.map.space.add(self.body, self.body_shape, self.hat_shape)

        self.held_baby = None
        self.is_midair = False
        self.is_climbing_ladder = False

    def destroy(self):
        self.map.space.remove(self.body, self.body_shape, self.hat_shape)
        del self.body_shape
        del self.hat_shape
        del self.held_baby
        del self.map

    def draw(self):
        self.sprite.position = self.body.position
        self.sprite.draw()
        if self.held_baby:
            if self.face_direction == 'right':
                self.held_baby.body.position = self.body.position + Vec2d(30,-10)
            elif self.face_direction == 'left':
                self.held_baby.body.position = self.body.position + Vec2d(-30,-10)
            self.held_baby.draw()

    def update(self, dt, keys):
        delta = Vec2d(0, 0)
        if keys[pyglet.window.key.RIGHT]:
            if self.is_climbing_ladder:
                self.body.position += Vec2d(200*dt,0)
            else:
                delta += Vec2d(1, 0)
                self.sprite = self.sprite_right
                self.face_direction = 'right'
        elif keys[pyglet.window.key.LEFT]:
            if self.is_climbing_ladder:
                self.body.position += Vec2d(-200*dt,0)
            else:
                delta += Vec2d(-1, 0)
                self.sprite = self.sprite_left
                self.face_direction = 'left'
        elif keys[pyglet.window.key.UP]:
            ladder = self.check_for_ladder()
            if self.is_climbing_ladder:
                self.sprite = self.sprite_climb
                if self.body.position.y < ladder.top - 20:
                    self.body.position += Vec2d(0,200*dt)
        elif keys[pyglet.window.key.DOWN]:
            self.check_for_ladder()
            if self.is_climbing_ladder:
                self.sprite = self.sprite_climb
                self.body.position += Vec2d(0,-200*dt)
        else:
            if self.sprite == self.sprite_right:
                self.sprite = self.sprite_idle_right
            elif self.sprite == self.sprite_left:
                self.sprite = self.sprite_idle_left

        if delta:
            self.body.apply_impulse(15000*dt*delta)

        if self.is_climbing_ladder:
            self.check_for_ladder()
            if self.is_climbing_ladder:
                self.body.velocity = Vec2d(0,0)
                self.body.reset_forces()
            else:
                self.sprite = self.sprite_idle_right
            
    def check_for_ladder(self):
        ladder = None
        shapes = self.map.space.point_query(self.body.position)
        for x in shapes:
            if hasattr(x, 'object') and isinstance(x.object(), Ladder):
                ladder = x.object()
                break
        if not ladder:
            self.is_climbing_ladder = False
        else:
            self.is_climbing_ladder = True
            self.is_midair = False
        return ladder

    def jump(self):
        if self.is_midair: return
        if self.is_climbing_ladder:
            self.is_climbing_ladder = False
            self.sprite = self.sprite_idle_right
            self.body.velocity = Vec2d(0,0)
        # This is to prevent jumping after you fall.
        # Has to be a high enough # that we can still jump on slopes.
        if abs(self.body.velocity.dot(Vec2d(0,1))) > 100: return
        delta = Vec2d(0,1)
        self.body.apply_impulse(20000*delta)
        self.is_midair = True

    def get_action_points(self, keys=None):
        if keys and keys[pyglet.window.key.DOWN]:
            return [self.body.position + Vec2d(0, -60),
                    self.body.position + Vec2d(-45, -60),
                    self.body.position + Vec2d(45, -60)]
        elif keys and keys[pyglet.window.key.UP]:
            return [self.body.position + Vec2d(0, 120)]
        elif self.face_direction == 'right':
            return [self.body.position + Vec2d(70, -10)]
        elif self.face_direction == 'left':
            return [self.body.position + Vec2d(-70, -10)]


    def first_object_at_points(self, points):
        for point in points:
            for shape in self.map.space.point_query(point):
                if hasattr(shape, 'object'):
                    return shape.object()
        return None
        

    def action(self, keys):
        if self.held_baby:
            if keys[pyglet.window.key.DOWN]:
                self.putdown_baby()
            elif keys[pyglet.window.key.UP]:
                self.stack_baby()
            else:
                self.throw_baby(keys)
        else:
            points = self.get_action_points(keys)
            hit_object = self.first_object_at_points(points)
            if hit_object:
                if isinstance(hit_object, Baby):
                    self.pickup_baby(hit_object)

    def weight(self):
        weight = 50
        if self.held_baby:
            weight += self.held_baby.weight()
        return weight

    def pickup_baby(self, baby):
        # Don't pick up if already holding a baby.
        if self.held_baby: return
        self.held_baby = baby
        self.map.remove_object(baby)
        baby.change_state('held')

    def throw_baby(self, keys):
        # Can't throw a baby that you don't have.
        if not self.held_baby: return
        baby = self.held_baby
        self.held_baby = None
        self.map.add_object(baby)
        if self.face_direction == 'right':
            if keys[pyglet.window.key.RIGHT]:
                angle = 30
            else:
                angle = 70
            delta = Vec2d(1,0).rotated_degrees(angle)
            baby.change_state('crawl_right')
        elif self.face_direction == 'left':
            if keys[pyglet.window.key.LEFT]:
                angle = 150
            else:
                angle = 110
            delta = Vec2d(1,0).rotated_degrees(angle)
            baby.change_state('crawl_left')
        baby.body.apply_impulse(4000*delta)

    def putdown_baby(self):
        """If you are holding a baby, place it beneath you and stand on it."""
        if not self.held_baby: return
        baby = self.held_baby
        self.held_baby = None
        self.map.add_object(baby)
        baby.change_state('crawl_' + self.face_direction)
        baby.change_state('idle')
        baby.body.position = self.body.position + Vec2d(0,-45)
        self.body.position += Vec2d(0,45)

    def stack_baby(self):
        """If you are holding a baby, stack it on top of your hat."""
        if not self.held_baby: return
        baby = self.held_baby
        self.held_baby = None
        self.map.add_object(baby)
        baby.change_state('crawl_' + self.face_direction)
        baby.change_state('idle')
        baby.body.position = self.body.position + Vec2d(0,112)


